package tv.acgn.advertisement.search.index.impl;

import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListSet;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import tv.acgn.advertisement.search.exception.AbstractAdSearchException;
import tv.acgn.advertisement.search.exception.IndexUpdateUnSupportException;
import tv.acgn.advertisement.search.index.IndexAware;
import tv.acgn.advertisement.search.indexobejct.AdUnitKeywordIO;

/**
 * @author : junG
 * @program : advertisement-ad-search-index
 * @description :  推广单元-兴趣标签索引操作实现类
 * @date : 2019-07-14 18:37
 **/

@Service
@Slf4j
public class InterestIndexImpl implements IndexAware<String, Set<String>> {

    /**
     * 反向索引对象 键为兴趣标签 interestTag； 值为推广单元识别码 adUnitId的集合Set
     */
    private static Map<String, Set<String>> itTagToUnitIdMap = new ConcurrentHashMap<>();

    /**
     * 正向索引对象 键为推广单元识别码 AdUnitId； 值为兴趣标签interestTag集合Set
     */
    private static Map<String, Set<String>> unitIdToItTagMap = new ConcurrentHashMap<>();

    /**
     * <h2>通过索引的键获取值</h2>
     *
     * @param s [1] 键
     *
     * @return {@link Set<String>} 索引值对象 adUnitId的集合Set
     *
     * @throws AbstractAdSearchException 异常
     * @author junG
     * @description 通过索引的键获取值
     * @date 22:31 2019/6/30
     */
    @Override
    public Set<String> get(String s) throws AbstractAdSearchException {
        return Optional.ofNullable(itTagToUnitIdMap.get(s))
            .orElseGet(Collections::emptySet);
    }

    /**
     * <h2>新增一条索引</h2>
     *
     * @param interestTag [1] 键
     * @param unitIds     [2] 值对象
     *
     * @return {@link Void}
     *
     * @throws AbstractAdSearchException 异常
     * @author junG
     * @description 新增一条索引
     * @date 22:32 2019/6/30
     */
    @Override
    @SuppressWarnings({"Duplicates"})
    public void add(String interestTag, Set<String> unitIds) throws AbstractAdSearchException {
        /**
         * 先添加 兴趣->推广单元 的索引
         */
        Set<String> unitIdSet = itTagToUnitIdMap.computeIfAbsent(interestTag, k -> (new ConcurrentSkipListSet<>()));

        unitIdSet.addAll(unitIds);
        /**
         * 再添加 推广单元->兴趣 的索引
         */
        unitIds.forEach(id -> {
            Set<String> interestTagSet = unitIdToItTagMap.computeIfAbsent(id, k -> (new ConcurrentSkipListSet<>()));
            interestTagSet.add(interestTag);
        });
    }

    /**
     * <h2>更新索引键对应的索引值对象</h2>
     *
     * @param s       [1]
     * @param strings [2]
     *
     * @return {@link Void}
     *
     * @throws AbstractAdSearchException 异常
     * @author junG
     * @description 更新索引键对应的索引值对象
     * @date 22:35 2019/6/30
     */
    @Override
    public void update(String s, Set<String> strings) throws AbstractAdSearchException {
        log.error("推广单元-兴趣索引不自持更新");
        throw new IndexUpdateUnSupportException(AdUnitKeywordIO.class);
    }

    /**
     * <h2>删除索引对象</h2>
     *
     * @description 关键词的索引删除不适用此方法
     * 请使用
     */
    @Override
    @Deprecated
    public void delete(String key) throws AbstractAdSearchException {

    }

    /**
     * <h2>删除索引对象的兴趣标签对应的推广单元识别码 UnitId</h2>
     *
     * @param interestTag [1] 推广单元关键词
     * @param unitIds     [2] 推广单元识别码集合
     *
     * @return {@link Void}
     *
     * @throws AbstractAdSearchException 异常
     * @author junG
     * @description 删除 兴趣标签 interestTag 到推广单元识别码UnitId的索引对象关系
     * @date 19:22 2019/7/14
     */
    public void deleteUnitId(String interestTag, Set<String> unitIds) throws AbstractAdSearchException {
        /*
            根据兴趣标签到对应的推广单元集合 然后删除掉对应的推广单元
         */
        Optional.ofNullable(itTagToUnitIdMap.get(interestTag))
            .ifPresent(x -> x.removeAll(unitIds));

        /*
         * 遍历推广单元->兴趣标签 删除此推广单元
         */
        unitIds.forEach(id -> {
            unitIdToItTagMap.remove(id);
        });
    }

    /**
     * <h2>删除兴趣标签</h2>
     *
     * @param interestTag [1] 兴趣标签
     *
     * @return {@link Void}
     *
     * @throws AbstractAdSearchException 异常
     * @author junG
     * @description 删除兴趣标签
     * @date 19:27 2019/7/14
     */
    public void deleteItTag(String interestTag) throws AbstractAdSearchException {
        log.info("删除索引对象itTagToUnitIdMap的兴趣标签 interestTag={}", interestTag);
        /*
         * 兴趣表删除兴趣标签
         */
        itTagToUnitIdMap.remove(interestTag);

        unitIdToItTagMap.forEach((k, v) -> {
            log.info("删除索引对象unitIdToIt的推广单元->兴趣标签关系 interestTag={}", interestTag);
            v.remove(interestTag);
        });
    }

    /**
     * 检测推广单元与传入的兴趣标签集合是否完全匹配
     *
     * @param unitId
     * @param interestTagList
     *
     * @return
     */
    public boolean match(String unitId, Set<String> interestTagList) {

        if (!CollectionUtils.isEmpty(unitIdToItTagMap.get(unitId))
            && unitIdToItTagMap.containsKey(unitId)) {
            return unitIdToItTagMap.get(unitId).containsAll(interestTagList);
        }
        return false;
    }

}
